/*
 * Lexer.java
 *
 * Copyright (C) 2013 UPOU.
 * IS214 - Programming Languages
 * All Rights Reserved.
 *
 * @author Mary Joy Pascual and Simon Zagado
 */

package com.upittc.flopol.core;

import com.upittc.flopol.constant.ErrorConstant;
import com.upittc.flopol.constant.FlopolConstant;
import com.upittc.flopol.core.beans.Token;
import com.upittc.flopol.core.helper.CoreHelper;

public class Lexer {
	
	public Token nextToken = new Token();
	public String text = ""; 	
	public String lexeme = "";
	public int nextIndex = 0;   
	public int tokenCount = 0;
	public int lineNo = 0;
	public int token = 0;

	public Lexer(String text){
		this.text = text;
		this.lineNo = 1;
	}
	
	public void lexer() throws Exception {	
		erase_spaces();
		erase_comment();
		if (!is_EOF()) {
			lexeme = "";
			token = 0;
			if(is_Letter()){
				while(is_Letter()){
					lexeme = lexeme + text.charAt(nextIndex);
					nextIndex++;
				}
				if(text.charAt(nextIndex) == FlopolConstant.QUESTION){
					lexeme = lexeme + text.charAt(nextIndex);
					nextIndex++;
					if(!is_NumPredicate(lexeme)){
						throw new Exception (CoreHelper.errorMessage(showLexemeError(), lineNo));
					} 
				} else {
					if(!is_EOF() && !is_Space()){
						throw new Exception (CoreHelper.errorMessage(showLexemeError(), lineNo));
					} 
					assign_token(lexeme);
				}
			} else if (is_Number()){ 
				while(is_Number()){
					lexeme = lexeme + text.charAt(nextIndex);
					nextIndex++;
				}
				if(text.charAt(nextIndex) == FlopolConstant.PERIOD){
					lexeme = lexeme + text.charAt(nextIndex);
					token =  FlopolConstant.FLT_LIT;
					nextIndex++;
					int decimalCount = 0;
					while(is_Number()){
						lexeme = lexeme + text.charAt(nextIndex);
						nextIndex++;
						decimalCount++;
					}
					if(decimalCount < 2){
						throw new Exception (CoreHelper.errorMessage(showLexemeError(), lineNo));
					} 
				}  else {
					throw new Exception (CoreHelper.errorMessage(showLexemeError(), lineNo));
				}
			} else if ( text.charAt(nextIndex) == FlopolConstant.DOLLAR){
				lexeme = Character.toString(FlopolConstant.DOLLAR);
				nextIndex++;
				while(text.charAt(nextIndex) != FlopolConstant.DOLLAR){
					lexeme = lexeme + text.charAt(nextIndex);
					token = FlopolConstant.STR_LIT;
					nextIndex++;
					if (is_EOF() || text.charAt(nextIndex) == FlopolConstant.NEW_LINE){
						throw new Exception(CoreHelper.errorMessage(showLexemeError(), lineNo));
					}
				}
				lexeme = lexeme + text.charAt(nextIndex);
				nextIndex++;
			} else if ( text.charAt(nextIndex)== FlopolConstant.RIGHT_BRA){
				lexeme = Character.toString(FlopolConstant.RIGHT_BRA);
				token = FlopolConstant.R_BR;
				nextIndex++;
			} else if ( text.charAt(nextIndex)== FlopolConstant.LEFT_BRA){
				lexeme = Character.toString(FlopolConstant.LEFT_BRA);
				token = FlopolConstant.L_BR;
				nextIndex++;
			} 
			if (is_EOF() || is_Space()) {
				tokenCount++;
				nextToken.setToken(token);
				nextToken.setLexeme(lexeme);
				CoreHelper.print(nextToken);
			} else {
				throw new Exception (CoreHelper.errorMessage(showLexemeError(), lineNo));
			}
		}
	}
	
	private void assign_token(String lexeme){
		if (is_Boolean(lexeme)){
			token = FlopolConstant.BOL_LIT;
		}else if (!is_Keyword(lexeme)){
			token = FlopolConstant.IDENT;
		} 
	}
	
	private void erase_spaces() {
		if (!is_EOF()) {
			while (is_Space()) {
				countLineNumber(text.charAt(nextIndex));
				nextIndex++;
				if (nextIndex >= text.length()){
					break;
				}
			}
		}	
	}
	
	private void erase_comment()throws Exception {
		if (!is_EOF()) {
			if(text.charAt(nextIndex) == FlopolConstant.ASTERISK){
				nextIndex++;
				while (text.charAt(nextIndex) != FlopolConstant.ASTERISK) {
					nextIndex++;
					if (is_EOF() || text.charAt(nextIndex) == FlopolConstant.NEW_LINE){
						throw new Exception(ErrorConstant.ER_TOKEN+ErrorConstant.ER_COMMENT);
					}
				}
				nextIndex++;
				erase_spaces();
			} 
		}	
	}
	
	private boolean is_Boolean(String lexeme){
		if(lexeme.equals(FlopolConstant.YEAH) || lexeme.equals(FlopolConstant.NOPE)){
			return true;
		} 
		return false;
	}
	
	private boolean is_Keyword(String lexeme){
		if(lexeme.equals(FlopolConstant.ADD) || lexeme.equals(FlopolConstant.SUB) 
				|| lexeme.equals(FlopolConstant.MULT) || lexeme.equals(FlopolConstant.DIVI) 
				|| lexeme.equals(FlopolConstant.EXP)  || lexeme.equals(FlopolConstant.REMI)){
			token = FlopolConstant.NUM_OP;
			return true;
		} else if (lexeme.equals(FlopolConstant.PING)) {
			token = FlopolConstant.UNARY_OP;
			return true;
		} else if (lexeme.equals(FlopolConstant.INTER) || lexeme.equals(FlopolConstant.UNION) ){
			token = FlopolConstant.LOG_OP;
			return true;
		} else if (lexeme.equals(FlopolConstant.NOT)){
			token = FlopolConstant.NOT_OP;
			return true;
		} else if(lexeme.equals(FlopolConstant.FLOP)){
			token = FlopolConstant.FLOP_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.CREATE)){
			token = FlopolConstant.CREATE_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.END)){
			token = FlopolConstant.END_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.IS)){
			token = FlopolConstant.IS_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.MANEUVER)){
			token = FlopolConstant.MAN_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.GIVE)){
			token = FlopolConstant.GIVE_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.GIVEMORE)){
			token = FlopolConstant.GVMR_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.BEG)){
			token = FlopolConstant.BEG_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.INTO)){
			token = FlopolConstant.INTO_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.INTO)){
			token = FlopolConstant.INTO_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.CHECK)){
			token = FlopolConstant.CHECK_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.TO)){
			token = FlopolConstant.TO_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.BUT)){
			token = FlopolConstant.BUT_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.TIMES)){
			token = FlopolConstant.TIMES_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.PERFORM)){
			token = FlopolConstant.PERFORM_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.FLOAT)){
			token = FlopolConstant.FLT_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.STRING)){
			token = FlopolConstant.STR_KEY;
			return true;
		} else if (lexeme.equals(FlopolConstant.BOOL)){
			token = FlopolConstant.BOL_KEY;
			return true;
		} 
		
		return false;
	}
	
	private boolean is_NumPredicate(String lexeme){
		if (lexeme.equals(FlopolConstant.GT) || lexeme.equals(FlopolConstant.GTE)
			|| lexeme.equals(FlopolConstant.LT) || lexeme.equals(FlopolConstant.LTE)){
			token = FlopolConstant.REL_OP;
			return true;
		} else if (lexeme.equals(FlopolConstant.EQ) || lexeme.equals(FlopolConstant.NEQ) ){
			token = FlopolConstant.EQNEQ_OP;
			return true;
		} return false;
	}
	
	private boolean is_Space(){
		if (!is_EOF()) {
			if(text.charAt(nextIndex) == FlopolConstant.SPACE || text.charAt(nextIndex) == FlopolConstant.NEW_LINE ||
					text.charAt(nextIndex) == FlopolConstant.TAB ){
				return true;
			} 
		}
		return false;
	}
	
	private boolean is_Letter() throws Exception {
		if (!is_EOF()) {
			if(Character.isLetter(text.charAt(nextIndex))){
				return true;
			} 
		}
		return false;
	}
	
	private boolean is_Number() {
		if (!is_EOF()) {
			if(Character.isDigit(text.charAt(nextIndex))){
				return true;
			}
		}
		return false;
	}
	
	private boolean is_EOF(){
		String eof = Character.toString(text.charAt(nextIndex));
		if (nextIndex == text.length() || eof.equals(FlopolConstant.EOF)) {
			nextToken.setToken(FlopolConstant.EOFF);
			return true;
		}
		return false;
	}
	
	private void countLineNumber(char a){
		if(a == FlopolConstant.NEW_LINE){
			lineNo ++;
			nextToken.setLineNo(lineNo);
		}
	}
	
	private String showLexemeError(){
		String lexError = lexeme;
		while (!is_Space() && !is_EOF()){
			lexError = lexError + text.charAt(nextIndex);
			nextIndex++; 
		}
		return lexError;
	}
	
	public void lexerTester() throws Exception{
		while (!is_EOF()){
			lexer();
		}
	}

	public Token getNextToken() {
		return nextToken;
	}

	
}
